home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Speccy ClassiX 1998
/
Speccy ClassiX 98.iso
/
amiga_system
/
the_aminet
/
dev
/
gcc
/
ixemulsrc.lha
/
ixemul-41.4
/
library
/
machdep.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-09-27
|
19KB
|
659 lines
/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* machdep.c,v 1.1.1.1 1994/04/04 04:30:40 amiga Exp
*
* machdep.c,v
* Revision 1.1.1.1 1994/04/04 04:30:40 amiga
* Initial CVS check in.
*
* Revision 1.5 1993/11/05 21:59:18 mwild
* add code to deal with inet.library
*
* Revision 1.4 1992/10/20 16:25:24 mwild
* no nasty 'c' polling in DEF-signalhandler...
*
* Revision 1.3 1992/08/09 20:57:59 amiga
* add volatile to sysbase access, or the optimizer takes illegal shortcuts...
*
* Revision 1.2 1992/07/04 19:20:20 mwild
* add yet another state in which not to force a context switch.
* Probably unnecessary paranoia...
*
* Revision 1.1 1992/05/14 19:55:40 mwild
* Initial revision
*
*/
#define KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <exec/execbase.h>
extern struct ExecBase *SysBase;
/* jump to pc in supervisor mode, usp is set to USP before */
extern void volatile supervisor (u_int pc, u_int usp);
/* context restore functions for 68000 and 68020 rsp */
/* takes the sigcontext * from the usp and restores it
* Assumes it's called by Supervisor(), ie. with an exception frame
*/
extern void volatile do_sigreturn (void);
/*
* These two are callable with jsr from supervisor mode, and then
* set up a fake exception frame and call do_sigreturn().
*/
extern void volatile sup00_do_sigreturn_ssp (u_int ssp);
extern void volatile sup00_do_sigreturn (void);
extern void volatile restore_00 ();
extern void volatile sup20_do_sigreturn_ssp (u_int ssp);
extern void volatile sup20_do_sigreturn (void);
extern void volatile restore_20 ();
/*
* Either one of sup{00,20}_do_sigreturn, set by configure_context_switch ();
*/
static void volatile (*sup_do_sigreturn) (void);
static void volatile (*sup_do_sigreturn_ssp) (u_int ssp);
void setrun (struct Task *t);
void sendsig(struct user *p, sig_t catcher, int sig, int mask, unsigned code, void *addr);
void sig_exit ();
/*
* NOTE: this variable belongs to ixemulbase, not to the current task. But
* due to the nature of launch/switch, we can access them here without
* locking. It quite a natural thing to do actually ;-)
*/
struct user *curproc;
struct sigframe {
int sf_signum; /* signo for handler */
int sf_code; /* additional info for handler */
void *sf_addr; /* yet another info for handler ;-)) */
sig_t sf_handler; /* handler addr for u_sigc */
struct sigcontext sf_sc; /* actual context */
};
void
configure_context_switch ()
{
if(betterthan68000())
{
sup_do_sigreturn = sup20_do_sigreturn;
sup_do_sigreturn_ssp = sup20_do_sigreturn_ssp;
}
else
{
sup_do_sigreturn = sup00_do_sigreturn;
sup_do_sigreturn_ssp = sup00_do_sigreturn_ssp;
}
}
void volatile
sigreturn (struct sigcontext *sc)
{
supervisor ((u_int) do_sigreturn, (u_int) sc);
}
void volatile
sig_trampoline (struct sigframe sf)
{
if (u.u_a4)
asm ("movel %0,a4" : : "g" (u.u_a4));
sf.sf_handler (sf.sf_signum, sf.sf_code, sf.sf_addr, & sf.sf_sc);
sigreturn (& sf.sf_sc);
}
/*
* This one is executed in Supervisor mode, just before dispatching this
* task, so be as quick as possible here !
*/
void
sig_launch ()
{
struct ExecBase *SysBase = *(struct ExecBase **) 4;
struct Task *me = SysBase->ThisTask;
/* precalculate struct user, so we don't have to go thru SysBase all the time */
struct user *p = (struct user *) me->tc_TrapData;
sigset_t sigmsg = sigmask (SIGMSG);
sigset_t sigint = sigmask (SIGINT);
sigset_t newsigs;
int i;
u_int usp, orig_usp;
struct sigcontext *sc;
u_int ret_pc, ret_ssp;
usp = orig_usp = get_usp () + 8; /* set up by our glue_launch() stub */
/* remember who we are */
curproc = p;
/* if we're inside ix_sleep, no signal processing is done to break the
Wait there as soon as possible. Signals resume on return of ix_sleep */
if (p->p_stat == SSLEEP)
return;
/* special processing for Wait()ing in Commodore inet.library. They
do reasonable interrupt checking, but only on SIGBREAKF_CTRL_C. So
whenever we have a signal to deliver, send ^C.. */
if (p->p_stat == SWAIT)
{
#if 0
/* not working clean yet, have to think of a more elaborate solution later */
/* handle ^C here, since we're going to unconditionally raise it
afterwards. */
if (!(p->p_sigignore & sigmask(SIGINT)) && SetSignal (0, SIGBREAKF_CTRL_C))
p->p_sig |= sigmask (SIGINT);
#endif
if (CURSIG (p))
Signal (me, SIGBREAKF_CTRL_C);
return;
}
/* smells kludgy I know...... */
if (me->tc_TDNestCnt >= 0 || me->tc_IDNestCnt >= 0)
return;
/* the glue passes us the values of the pc and ssp to restore, if we should
* decide to sup_do_sigreturn_ssp() out of here, instead of leaving harmlessly..
*/
ret_pc = ((u_int *)usp)[-2];
ret_ssp = ((u_int *)usp)[-1];
/* push a sigcontext that will get us back if no other signals
* were produced */
usp -= sizeof (struct sigcontext);
sc = (struct sigcontext *) usp;
set_usp (usp);
sc->sc_onstack = p->u_onstack;
sc->sc_mask = p->p_sigmask;
sc->sc_sp = orig_usp;
/* the OS context restore function expects a5 to contain the usp, so
* we have to obey.. */
sc->sc_fp = orig_usp;
sc->sc_ap = *(u_int *)&me->tc_Flags;
sc->sc_pc = ret_pc;
sc->sc_ps = get_sr ();
/*
* first check amigados signals. If SIGMSG is set to SIG_IGN or SIG_DFL,
* we do our default mapping of SIGBREAKF_CTRL_C into SIGINT.
*/
newsigs = me->tc_SigRecvd &~ p->u_lastrcvsig;
p->u_lastrcvsig = me->tc_SigRecvd;
if (p->u_InetBase)
{
int urgmask = 1<<p->u_sigurg;
int iomask = 1<<p->u_sigio;
if (newsigs & urgmask)
{
if (! (p->p_sigignore & sigmask (SIGURG)))
_psignal (me, SIGURG);
me->tc_SigRecvd &= ~urgmask;
p->u_lastrcvsig &= ~urgmask;
}
if (newsigs & iomask)
{
if (! (p->p_sigignore & sigmask (SIGIO)))
_psignal (me, SIGIO);
me->tc_SigRecvd &= ~iomask;
p->u_lastrcvsig &= ~iomask;
}
}
if (((p->p_sigignore & sigmsg) || !(p->p_sigcatch & sigmsg))
&& (newsigs & SIGBREAKF_CTRL_C))
{
/* in that case send us a SIGINT, if it's not ignored */
if (!(p->p_sigignore & sigint))
_psignalgrp((struct Process *)me, SIGINT);
/* in this mode we fully handle and use SIGBREAKF_CTRL_C, so remove it
* from the Exec signal mask */
me->tc_SigRecvd &= ~SIGBREAKF_CTRL_C;
p->u_lastrcvsig &= ~SIGBREAKF_CTRL_C;
}
else if (newsigs && (p->p_sigcatch & sigmsg))
{
/* if possible, deliver the signal directly to get a code argument */
if (!(p->p_flag & STRC) && !(p->p_sigmask & sigmsg))
{
p->u_ru.ru_nsignals++;
sendsig(p, p->u_signal[SIGMSG], SIGMSG, p->p_sigmask, newsigs, 0);
p->p_sigmask |= p->u_sigmask[SIGMSG] | sigmsg;
setrun (me);
}
else
_psignal (me, SIGMSG);
}
/* stack watch.. NEVER do this when vforked, the sp is out of bounds then.. */
if (ix.ix_watch_stack && p->u_red_zone && ! p->p_vfork_msg && ! p->u_onstack)
{
if (((void *) usp) < p->u_red_zone)
_psignal (me, SIGSEGV);
}
if (i = CURSIG(p))
psig (p, i);
/* now try to optimize. We could always call sup_do_sigreturn here, but if no
* signals generated frames, we can just as well simply return, after having
* restored our usp */
if (usp == get_usp ())
{
/* this is probably not even necessary, since after processing sig_launch
* the OS reinstalls the usp as me->tc_SPReg, but I guess it's cleaner to
* do it explicitly here, to show that we reset usp to what it was before
*/
set_usp (orig_usp);
return;
}
sup_do_sigreturn_ssp (ret_ssp);
}
/*
* nothing magic about this... when leaving, we note that no process is currently
* executing (at least none under our control ;-))
*/
void
switch_glue ()
{
curproc = 0;
}
/*
* Send an interrupt to process.
* Called from psig() which is called from sig_launch, thus we are in
* SUPERVISOR .
*/
void
sendsig (struct user *p, sig_t catcher, int sig, int mask, unsigned code, void *addr)
{
struct ExecBase *SysBase = *(struct ExecBase **) 4;
struct Task *me = SysBase->ThisTask;
u_int usp, orig_usp;
struct sigframe *sf;
struct sigcontext *sc;
int oonstack;
orig_usp = get_usp(); /* get value to restore later */
oonstack = p->u_onstack;
if (!p->u_onstack && (p->u_sigonstack & sigmask(sig)))
{
p->u_onstack = 1;
usp = (u_int) p->u_sigsp;
}
else
usp = orig_usp;
/* push signal frame */
usp -= sizeof (struct sigframe);
sf = (struct sigframe *) usp;
/* fill out the frame */
sf->sf_signum = sig;
sf->sf_code = code;
sf->sf_addr = addr;
sf->sf_handler = catcher;
sf->sf_sc.sc_onstack = oonstack;
sf->sf_sc.sc_mask = mask;
sf->sf_sc.sc_sp = (int) orig_usp; /* previous sigcontext */
sf->sf_sc.sc_fp = 0;
sf->sf_sc.sc_ap = *(u_int *)&me->tc_Flags;
sf->sf_sc.sc_ps = get_sr (); /* we're in supervisor then */
sf->sf_sc.sc_pc = (int) sup_do_sigreturn;/* this pc will restore it */
/* push a signal context to call sig_trampoline */
usp -= sizeof (struct sigcontext);
sc = (struct sigcontext *) usp;
/*
* NOTE: we set the default of a handler to Permit(), Enable(). I guess this
* makes sense, since if either Forbid() or Disable() is active, it
* shouldn't be possible to invoke a signal anyway, EXCEPT if the
* task is Wait()ing, then the OS calls Switch() directly while
* either Disable() or Forbid() is active (depends on OS version).
*/
sc->sc_onstack = p->u_onstack;
sc->sc_mask = p->p_sigmask;
sc->sc_sp = ((int) sf) - 4; /* so that sp@(4) is the argument */
sc->sc_fp = 0;
sc->sc_ap = (me->tc_Flags << 24) | (me->tc_State << 16) |
((u_char)(-1) << 8) | (u_char)(-1);
sc->sc_ps = 0; /* thus we switch into user-mode now! */
sc->sc_pc = (int) sig_trampoline;
set_usp (usp);
}
/*
* called as the default action of a signal that terminates the process
*/
void
sig_exit (unsigned int code)
{
#ifndef QUIET_SIGEXIT
/* the whole purpose of this code inside `ifndef QUIET_SIGEXIT' is to
* prettyprint and identify the job that just terminates
* This stuff should be handled by a shell, but since there's (yet) no
* shell that knows how to interpret a signal-exit code (ored with 0x80)
* I have to do it here myself...
*/
extern char *sys_siglist[NSIG];
struct Process *me = (struct Process *)((*(struct ExecBase **)4)->ThisTask);
char err_buf[255];
struct CommandLineInterface *cli;
char process_name[255];
int is_fg;
/* output differs depending on
* o whether we're a CLI or a WB process (stderr or requester)
* o whether this is a foreground or background process
* o whether this is SIGINT or not
*/
/* make sure we're not interrupted in this last step.. */
syscall (SYS_sigsetmask, ~0);
if (cli = BTOCPTR (me->pr_CLI))
{
char *tmp = BTOCPTR (cli->cli_CommandName);
int len = *tmp++;
if (len > sizeof (process_name) - 1)
len = sizeof (process_name) - 1;
bcopy (tmp, process_name, len);
process_name[len] = 0;
is_fg = cli->cli_Interactive && !cli->cli_Background;
}
else
{
process_name[0] = 0;
if (me->pr_Task.tc_Node.ln_Name)
strncpy (process_name, me->pr_Task.tc_Node.ln_Name, sizeof (process_name) - 1);
/* no WB process is ever considered fg */
is_fg = 0;
}
/* if is_fg and SIGINT, simulate tty-driver and display ^C */
if (!(is_fg && (code == SIGINT)))
{
strcpy (err_buf, (code < NSIG) ? sys_siglist[code] : "Unknown signal");
/* if is_fg, don't display the job */
if (! is_fg)
{
strcat (err_buf, " - ");
strcat (err_buf, process_name);
/* if we're a CLI we have an argument line saved, that we can print
* as well */
if (cli)
{
int line_len;
char *cp;
/* we can display upto column 77, this should be save on all normal
* amiga CLI windows */
line_len = 77 - strlen (err_buf) - 1;
if (line_len > u.u_arglinelen)
line_len = u.u_arglinelen;
if (line_len > 0 && u.u_argline)
{
strcat (err_buf, " ");
strncat (err_buf, u.u_argline, line_len);
}
/* now get rid of possible terminating line feeds/cr's */
for (cp = err_buf; *cp && *cp != '\n' && *cp != '\r'; cp++) ;
*cp = 0;
}
}
if (cli)
{
/* uniformly append ONE line feed */
strcat (err_buf, "\n");
syscall (SYS_write, 2, err_buf, strlen (err_buf));
}
else
ix_panic (err_buf);
}
else
syscall (SYS_write, 2, "^C\n", 3);
#endif
syscall (SYS_exit, code | 0x80);
/* not reached */
}
/*
* This is used to awaken a possibly sleeping sigsuspend()
* and to force a context switch, if we send a signal to ourselves
*/
void
setrun (struct Task *t)
{
struct user *p = (struct user *) t->tc_TrapData;
volatile struct ExecBase *SysBase = *(volatile struct ExecBase **)4;
u_int curr_disp;
u_int sr;
/* NOTE: the context switch is done to make sure sig_launch() is called as
* soon as possible in the respective task. It's not nice if you can
* return from a kill() to yourself, before the signal handler had a
* chance to react accordingly to the signal..
*/
asm volatile ("
movel a5,a0
lea Lget_sr,a5
movel 4:w,a6
jsr a6@(-0x1e)
movel a1,%0
bra Lskip
Lget_sr:
movew sp@,a1 | get sr register from the calling function
rte
Lskip:
movel a0,a5
" : "=g" (sr) : : "a0", "a1", "a6");
/* Don't force context switch if:
o running in Supervisor mode
o we setrun() some other process
o running under either Forbid() or Disable() */
if ((sr & 0x2000) || SysBase->ThisTask != t || p->p_stat == SSLEEP ||
p->p_stat == SWAIT || SysBase->TDNestCnt >= 0 || SysBase->IDNestCnt >= 0)
{
extern int select();
/* make testing of p_stat and reaction atomic */
Disable ();
if (p->p_stat == SWAIT)
Signal (t, SIGBREAKF_CTRL_C);
else if (p->p_wchan == (caddr_t) p)
{
KPRINTF (("setrun $%lx\n", p));
ix_wakeup (p);
}
else if (p->p_wchan == (caddr_t) select)
Signal (t, 1<<p->u_sleep_sig);
Enable ();
return;
}
for (curr_disp = SysBase->DispCount; curr_disp == SysBase->DispCount; ) ;
/* this quite brute-force method, but the only thing I could think of that
* really guarantees that there was a context switch.. */
}
/*
* Mapping from vector numbers into signals
*/
const static int hwtraptable[256] = {
SIGILL, /* Reset initial stack pointer */
SIGILL, /* Reset initial program counter */
SIGBUS, /* Bus Error */
SIGBUS, /* Address Error */
SIGILL, /* Illegal Instruction */
SIGFPE, /* Zero Divide */
SIGFPE, /* CHK, CHK2 Instruction */
SIGFPE, /* cpTRAPcc, TRAPcc, TRAPV Instruction */
SIGILL, /* Privilege Violation */
SIGTRAP,/* Trace */
SIGEMT, /* Line 1010 Emulator */
SIGEMT, /* Line 1111 Emulator */
SIGILL,
SIGILL, /* Coprocessor Protocol Violation */
SIGILL, /* Format Error */
SIGILL, /* Uninitialized Interrupt */
SIGILL, /* 16 */
SIGILL, /* 17 */
SIGILL, /* 18 */
SIGILL, /* 19 */ /* unimplemented, reserved */
SIGILL, /* 20 */
SIGILL, /* 21 */
SIGILL, /* 22 */
SIGILL, /* 23 */
SIGILL, /* spurious Interrupt */
SIGILL, /* Level 1 Interrupt Autovector */
SIGILL, /* Level 2 Interrupt Autovector */
SIGILL, /* Level 3 Interrupt Autovector */
SIGILL, /* Level 4 Interrupt Autovector */
SIGILL, /* Level 5 Interrupt Autovector */
SIGILL, /* Level 6 Interrupt Autovector */
SIGILL, /* Level 7 Interrupt Autovector */
SIGILL, /* Trap #0 (not available on Unix) */
SIGILL, /* Trap #1 */
SIGILL, /* Trap #2 */
SIGILL, /* Trap #3 */
SIGILL, /* Trap #4 */
SIGILL, /* Trap #5 */
SIGILL, /* Trap #6 */
SIGILL, /* Trap #7 */
SIGILL, /* Trap #8 */
SIGILL, /* Trap #9 */
SIGILL, /* Trap #10 */
SIGILL, /* Trap #11 */
SIGILL, /* Trap #12 */
SIGILL, /* Trap #13 */
SIGILL, /* Trap #14 */
SIGILL, /* Trap #15 (not available on Unix) */
SIGFPE, /* FPCP Branch or Set on Unordererd Condition */
SIGFPE, /* FPCP Inexact Result */
SIGFPE, /* FPCP Divide by Zero */
SIGFPE, /* FPCP Underflow */
SIGFPE, /* FPCP Operand Error */
SIGFPE, /* FPCP Overflow */
SIGFPE, /* FPCP Signaling NAN */
SIGILL,
SIGBUS, /* MMU Configuration Error */
SIGILL, /* MMU Illegal Operation (only 68851) */
SIGILL, /* MMU Privilege Violation (only 68851) */
/* rest undefined or free user-settable.. */
};
/*
* handle traps handled over from the lowlevel trap handlers
*/
void
trap (u_int format, void *addr)
{
struct ExecBase *SysBase = *(struct ExecBase **) 4;
struct Task *me = SysBase->ThisTask;
/* precalculate struct user, so we don't have to go thru SysBase all the time */
struct user *p = (struct user *) me->tc_TrapData;
int sig;
u_int usp, orig_usp;
struct sigcontext *sc;
#if __GNUC__ != 2 || defined(BROKEN_GCC20)
int volatile been_here;
#endif
u_int ret_pc, ret_ssp;
usp = orig_usp = get_usp () + 8; /* skip argument parameters */
ret_pc = ((u_int *)usp)[-2];
ret_ssp = ((u_int *)usp)[-1];
/* push a sigcontext that will get us back here if no other signals
* were produced */
usp -= sizeof (struct sigcontext);
sc = (struct sigcontext *) usp;
set_usp (usp);
sc->sc_onstack = p->u_onstack;
sc->sc_mask = p->p_sigmask;
sc->sc_sp = orig_usp;
sc->sc_fp = get_fp ();
sc->sc_ap = *(u_int *)&me->tc_Flags;
sc->sc_pc = ret_pc;
sc->sc_ps = get_sr ();
/* format contains the vector * 4, in the lower 12 bits */
sig = *(int *)((u_char *)hwtraptable + (format & 0x0fff));
trapsignal (me, sig, format, addr);
if (sig = CURSIG(p))
psig (p, sig);
/* now try to optimize. We could always call sup_do_sigreturn here, but if no
* signals generated frames, we can just as well simply return, after having
* restored our usp */
if (usp == get_usp ())
{
set_usp (orig_usp);
return;
}
sup_do_sigreturn_ssp (ret_ssp);
}